#include "Matrix4.h"
#include <cassert>
#include <cmath>


const float  Matrix4::EQUALLIMIT = 0.0001f;
const Matrix4 Matrix4::ZERO;
const Matrix4 Matrix4::IDENTITY( 1.0f, 0,    0,    0,
							   0,    1.0f, 0,    0,
							   0,    0,    1.0f, 0,
							   0,    0,    0,    1.0f );

Matrix4::Matrix4()
{
	for( unsigned int i = 0; i < ROWCOUNT; ++i )
	{
		for( unsigned int j = 0; j < COLUMNCOUNT; ++j )
		{
			mElement[i][j] = 0;
		}
	}
}

Matrix4::Matrix4( const Matrix4& m )
{
	for( unsigned int i = 0; i < ROWCOUNT; ++i )
	{
		for( unsigned int j = 0; j < COLUMNCOUNT; ++j )
		{
			mElement[i][j] = m.mElement[i][j];
		}
	}
}

Matrix4::Matrix4( float _00, float _01, float _02, float _03,
				float _10, float _11, float _12, float _13,
				float _20, float _21, float _22, float _23,
				float _30, float _31, float _32, float _33 )
{
	mElement[0][0] = _00;
	mElement[0][1] = _01;
	mElement[0][2] = _02;
	mElement[0][3] = _03;

	mElement[1][0] = _10;
	mElement[1][1] = _11;
	mElement[1][2] = _12;
	mElement[1][3] = _13;

	mElement[2][0] = _20;
	mElement[2][1] = _21;
	mElement[2][2] = _22;
	mElement[2][3] = _23;

	mElement[3][0] = _30;
	mElement[3][1] = _31;
	mElement[3][2] = _32;
	mElement[3][3] = _33;
}

Matrix4::Matrix4( const float data[ELEMENTCOUNT] )
{
	for( unsigned int i = 0; i < ELEMENTCOUNT; ++i )
	{
		mElement2[i] = data[i];
	}
}

Matrix4::Matrix4( const int data[ELEMENTCOUNT] )
{
	for( unsigned int i = 0; i < ELEMENTCOUNT; ++i )
	{
		mElement2[i] = (float)data[i];
	}
}

Matrix4::~Matrix4()
{
}

Matrix4 Matrix4::operator+( const Matrix4& m ) const
{
	Matrix4 m1( *this );
	m1 += m;

	return m1;
}

Matrix4 Matrix4::operator-( const Matrix4& m ) const
{
	Matrix4 m1( *this );
	m1 -= m;

	return m1;
}

Matrix4 Matrix4::operator*( const Matrix4& m ) const
{
	Matrix4 m1( *this );
	m1 *= m;

	return m1;
}

Matrix4 Matrix4::operator*( float s ) const
{
	Matrix4 m1( *this );
	m1 *= s;

	return m1;
}

Matrix4 Matrix4::operator/( float s ) const
{
	assert( s );

	Matrix4 m1( *this );
	m1 /= s;

	return m1;
}

Matrix4& Matrix4::operator=( const Matrix4& m )
{
	for( int i = 0; i < ROWCOUNT; ++i )
	{
		for( int j = 0; j < COLUMNCOUNT; ++j )
		{
			mElement[i][j] = m.mElement[i][j];
		}
	}

	return *this;
}

Matrix4& Matrix4::operator+=( const Matrix4& m )
{
	for( int i = 0; i < ROWCOUNT; ++i )
	{
		for( int j = 0; j < COLUMNCOUNT; ++j )
		{
			mElement[i][j] += m.mElement[i][j];
		}
	}

	return *this;
}

Matrix4& Matrix4::operator-=( const Matrix4& m )
{
	for( int i = 0; i < ROWCOUNT; ++i )
	{
		for( int j = 0; j < COLUMNCOUNT; ++j )
		{
			mElement[i][j] -= m.mElement[i][j];
		}
	}

	return *this;
}

Matrix4& Matrix4::operator*=( const Matrix4& m )
{
	for( int i = 0; i < ROWCOUNT; ++i )
	{
		for( int j = 0; j < COLUMNCOUNT; ++j )
		{
			mElement[i][j] = GetRowVector( i ).Dot( m.GetColumnVector( j ) );
		}
	}

	return *this;
}

Matrix4& Matrix4::operator*=( float s )
{
	for( int i = 0; i < ROWCOUNT; ++i )
	{
		for( int j = 0; j < COLUMNCOUNT; ++j )
		{
			mElement[i][j] *= s;
		}
	}

	return *this;
}

Matrix4& Matrix4::operator/=( float s )
{
	assert( s );

	for( int i = 0; i < ROWCOUNT; ++i )
	{
		for( int j = 0; j < COLUMNCOUNT; ++j )
		{
			mElement[i][j] /= s;
		}
	}

	return *this;
}

bool Matrix4::operator==( const Matrix4& m ) const
{
	for( int i = 0; i < ROWCOUNT; ++i )
	{
		for( int j = 0; j < COLUMNCOUNT; ++j )
		{
			if( fabs( mElement[i][j] - m.mElement[i][j] ) > EQUALLIMIT ) return false;
		}
	}

	return true;
}

bool Matrix4::operator!=( const Matrix4& m ) const
{
	return ! ( *this == m );
}

Vector4 Matrix4::GetRowVector( unsigned int row ) const
{
	Vector4 v( mElement[row][0], mElement[row][1], mElement[row][2], mElement[row][3] );
	return v;
}

Vector4 Matrix4::GetColumnVector( unsigned int column ) const
{
	Vector4 v( mElement[0][column], mElement[1][column], mElement[2][column], mElement[3][column] );
	return v;
}

Matrix4 operator*( float s, const Matrix4& m )
{
	return m * s;
}

std::ostream& operator<<( std::ostream& o, const Matrix4& m )
{
	o << std::endl;

	for( int i = 0; i < Matrix4::ROWCOUNT; ++i )
	{
		for( int j = 0; j < Matrix4::COLUMNCOUNT; ++j )
		{
			o << m.mElement[i][j];
			if( j == Matrix4::COLUMNCOUNT - 1 ) o << std::endl;
			else o << ", ";
		}
	}

	o << std::endl;
	return o;
}

